#!/usr/bin/python3
# -*- coding: UTF-8 -*- 
# 设置utf-8  显示中文
"""
@Create Time: 2021/1/15 上午9:54
@Author: guo
@File：check_dependence_data_yml.py
"""
import pytest

from common.yml.get_yml_keys import GetYmlKeys
from config.get_conf_data import GetConfData


class CheckYml:
    """
    校验dependence_case_data里的数据.当dependence_type为request或response时的校验.  \n
    包括对 case_id的校验(是否有值,格式的书写是否正确等),以及 dependence_key 和 replace_key 的校验等  \n
    """

    def __init__(self):
        self.__conf = GetConfData()
        # case层连接page层的分割标志为":::" 3个:
        self.__case_join_page_flag = self.__conf.get_case_join_page()
        # yml文件连接case 或 page层的方法的标志为"::" 2个:
        self.__yml_join_func_flag = self.__conf.get_yml_join_func()
        # 多个key之间的连接标志为"+"  加号
        self.__key_join_flag = self.__conf.get_key_join()

    def check_data(self,page_apidata:dict,case_apidata:dict,ymlkeys:GetYmlKeys)->str:
        """
        1:dependence_key 和 replace_key 的值必须为list,否则直接将case置为fail  \n
        2:当两者个数不相等,或有个数为0时,将case置为fail  \n
        3:case_id 的值分为两部分,一部分由page层业务维护,一分部由case层数据层维护,即case_id的值,由两部分合成  \n
        4:case_id 的规则如下:  \n
            a: page层维护 page层的caseid.  \n
            b: case层维护 case层的caseid.  \n
            c: 当需要依赖登录时,page层的caseid为None,case层 需要维护login所在的yml文件及获取登录结果的方法名称,如test_data.yml:test_init  \n
            d: dependence_key 和 replace_key 之前是由 page层进行维护.现在迁移到case层进行维护.  \n
            e: dependence_key 和 replace_key 若是还是放在page层进行维护,则在case层里,不能写该字段.否则就会将page层的值替换为case层的值  \n
        在校验的过程中,一旦出现校验不通过,则直接将case置为fail.  \n
        :param page_apidata: page层的yml数据
        :param case_apidata: case层的yjl数据
        :param ymlkeys: yml数据里的key
        :return: 拼接后的caseid,暂时不考虑 list 类型数据.
        """

        """
        下面为之前的逻辑
        1:之前的依赖的case为list,现在变更为一个str,暂不考虑依赖多个接口的情况,该情况后续再解决 
        2:会先判断 dependence_key 与 replace_key 两者是否均为list,且两者的个数是否均相等,且不为0  
        3:当两者类型不匹配时,如有一个不为list一个为str时,或个数不相等,要将该case置为fail 
        4:当两者均为str时,要进行下面的判断: 
            a:如果依赖同一个接口的多个值时,dependence_key及replace_key的值之间可以使用+号进行拼接(会先判断是否为None) 
            b:如dependece_key: $.userid+$.name+$.sex replace_key: userId+Name+Sex 
            c:当有+号时,要判断dependence_key的值切割后的list的长度是否与replacekey的值切割后的list的长度一致 
            d:若不一致时,则直接将该case置为fail 
            e:当没有+号时,则需要先获取各自的值,然后再添加到一个临时的list里,最后再将list重新赋值给dependence_key 和 replace_key 
            f:case_id的值分为两部分,一部分由page业务层维护,一部分由case数据层维护,即case_id的值由两部分合成 
            g:case_id 目前的规则如下: 
                1:page层维护page层的caseid,以及dependence_key 和 replace_key  
                2:case层维护page层的caseid  
                3:当需要依赖登录时,page层的caseid为None,case层 需要维护login所在的yml文件及获取登录结果的方法名称,如test_data.yml:test_init 
        """

        api_data = page_apidata
        yml_keys = ymlkeys
        req_params = case_apidata

        caseid_value = None

        # dependence_case_data 的key值
        key = yml_keys.get_dependence_case_data_key()


        case_id_key = yml_keys.get_dependence_case_id_key()
        # page业务层的 依赖的case 数据
        data_page = api_data[key][case_id_key]
        # case数据层的 依赖的case 数据
        data_case = req_params[key][case_id_key]
        """
        case_id的值拼接后主要有下面三种场景:
        1: case层:::page层 ,最常见的
        2: None:::page层, 主要是体现在查询数据, 经过多次的验证,目前这种场景不多见,可以忽略,该框架不支持
        即当case层出现值为None时,直接将该case置为fail,不在运行该接口
        3: case层:::None ,主要是登录. 此时要检测case层的case的名称是否为登录的方法名称,若不为登录时,则
        将当前的这个case置为fail,不在运行该接口.目前不考虑登录之外的情况. 
        """
        # 登录的方法名称
        login_func = self.__conf.get_test_init_func_name()
        if data_case == None:
            pytest.fail(f"case数据层的依赖部分为None,但是该接口有数据依赖,所以依赖数据不全,"
                        f"故将该用例置为fail")
        elif data_page == None:
            # 当page业务层为None时,要判断case业务层的登录的名称是否为指定的登录,否则也置为fail
            # # 获取yml文件连接func的标志
            # yml_join_func = self.__conf.get_yml_join_func()

            # 判断yml连接func的标志是否存在
            if self.__check_yml_join_func(data_case):
                func_name = data_case.split(self.__yml_join_func_flag)[1]
                # 判断获取登录结果的方法名称是否与配置文件里指定的方法名称一致.
                if login_func == func_name:
                    api_data[key][case_id_key] = data_case + self.__case_join_page_flag + "None"
                    caseid_value = data_case + self.__case_join_page_flag + "None"
                else:
                    pytest.fail(f"case数据层的获取登录结果的方法名称书写错误,配置文件里规定的名称为{login_func},"
                                f"而实际的书写的值为{func_name},故将该用例置为fail")
            else:
                pytest.fail(f"case数据层的依赖部分格式书写错误,当前的值为{data_case},未找到yml文件与case名称的"
                            f"连接标志{self.__yml_join_func_flag}故将该用例置为fail")
        # 表示caes层与page都有数据
        else:
            # 要对case层和page业务层 检测是否有yml连接func的标志
            data_case_bool = self.__check_yml_join_func(data_case)
            data_page_bool = self.__check_yml_join_func(data_page)
            # 表示格式的书写都正确
            if data_page_bool == True and data_case_bool == True:
                """"""
                # 进行case数据层与page业务层的数据拼接
                api_data[key][case_id_key] = data_case + self.__case_join_page_flag + data_page
                caseid_value = data_case + self.__case_join_page_flag + data_page

            elif data_case_bool == False and data_page_bool == False:
                pytest.fail(f"case数据层的参数值书写错误,未找到yml文件与case名称的连接标志{self.__yml_join_func_flag},\n"
                            f"case数据层的数据为:{data_case}\n"
                            f"page业务层的值书写错误,未找到yml文件与func()方法名称的连接标志{self.__yml_join_func_flag},\n"
                            f"page业务层的数据为:{data_page}\n"
                            f"故将该用例置为fail")
            elif data_case_bool == False:
                pytest.fail(f"case数据层的参数值书写错误,未找到yml文件与case名称的连接标志{self.__yml_join_func_flag},\n"
                            f"case数据层的数据为:{data_case}\n"
                            f"page业务层的值书写正确,找到yml文件与func()方法名称的连接标志{self.__yml_join_func_flag},\n"
                            f"page业务层的数据为:{data_page}\n"
                            f"故将该用例置为fail")
            else:
                pytest.fail(f"page业务层的值书写错误,未找到yml文件与case名称的连接标志{self.__yml_join_func_flag},\n"
                            f"page业务层的数据为:{data_page}\n"
                            f"case数据层的参数值书写正确,找到yml文件与func()方法名称的连接标志{self.__yml_join_func_flag},\n"
                            f"case数据层的数据为:{data_case}\n"
                            f"故将该用例置为fail")
        """
        1：只要dependence_key、replace_key 有一个为空时，表示依赖数据书写错误，此时要将case置为fail
        2：只有当dependence_key 、 replace_key 两者均不为空时，要进行如下的判断
            a: dependence_key 、replace_key 均存在多值时，只在两者切割后的list的值相等时，则表示参数书写正确
            b：dependence_key 、replace_key 均不存在多值的情况下(即不存在"+"），参数书写正确,但是要将其值转化为list
            c：dependence_key 、replace_key 一个存在多值，另一个不存在多值，则表示参数书写不正确，将case置为fail
            否则就将case置为fail
        2021-01-15 将逻辑更改为:
        1：只要dependence_key、replace_key 必须都为list,否则将case置为fail
        2：只有当dependence_key 、 replace_key 两都的个数都相等,且均不为0,否则将case置为fail
        """
        # 进行 dependence_key 与 replace_key 的校验
        dependence_key = api_data[key][yml_keys.get_dependence_key()]
        replace_key = api_data[key][yml_keys.get_replace_key()]
        # 先判断是否都不为空，即都有值的情况下
        if dependence_key != None and replace_key != None:
            """
            先判断是否为list,当两者都为list时,再判断两者的个数是否相等
            若不相等,则将该case置为fail
            """
            # 两者均为list
            if isinstance(dependence_key, list) and isinstance(replace_key, list):
                dependence_count = len(dependence_key)
                replace_count = len(replace_key)
                # 表示两者的个数一致.要再进行一步判断个数是否为0
                if dependence_count == replace_count:
                    # 再判断个数是否为0
                    if dependence_count == 0:
                        pytest.fail(f"dependence_key 与 replace_key 的类型均为list,"
                                    f"但其个数均为: {dependence_count},即值写错了,故将case置为fail.")
                    # 表示书写正确.
                    else:
                        """表示书写正确,不进行任何的操作"""
                        pass

                # 表示两者的个数不一致,要将case置为fail
                else:
                    pytest.fail(f"dependence_key 的类型为list,其个数为: {dependence_count},"
                                f"replace_key 的类型为list,其个数为: {replace_count},"
                                f"两者的个数不一致,故将case置为fail.")


            # dependence_key 与 replace_key 两者,至少有一个不为list
            else:
                pytest.fail(f"dependence_key 的类型 与 replace_key 的类型,两者至少有一个不为list,不符合书写规范,"
                            f"故将case置为 fail. 两者的类型信息如下:\n"
                            f"dependence_key 是否为list: {isinstance(dependence_key, list)},"
                            f"replace_key 是否为list: {isinstance(replace_key, list)} \n")

        # dependence_key 与 replace_key 均为空
        elif dependence_key == None and replace_key == None:
            pytest.fail(f"依赖的字段(dependence_key)的值为{dependence_key}，即依赖的字段书写错误"
                        f"替换的字段(replace_key)的值为{replace_key},即替换的字段书写错误"
                        f"故将case置为fail")

        # dependence_key 为空
        elif dependence_key == None:
            pytest.fail(f"依赖的字段(dependence_key)的值为{dependence_key}，即依赖的字段书写错误"
                        f"替换的字段(replace_key)的值为{replace_key},"
                        f"两者的值不匹配，故将case置为fail")

        # replace_key 为空
        else:
            pytest.fail(f"依赖的字段(dependence_key)的值为{dependence_key}，"
                        f"替换的字段(replace_key)的值为{replace_key},即替换的字段书写错误"
                        f"两者的值不匹配，故将case置为fail")

        # 目录返回是的是str,后续考虑换为list.
        return caseid_value

    def __check_yml_join_func(self, casedata: str) -> bool:
        """检测case层和page层的数据是否有yml连接func的标志"""
        # # 获取yml文件连接func的标志
        # yml_join_func = self.__conf.get_yml_join_func()
        flag = False
        if self.__yml_join_func_flag in casedata:
            flag = True
        return flag

    # def __check_key_join(self, keystr: str):
    #     """检测dependence_key 及 replace_key 的值里是否有连接标志"""
    #     flag = False
    #     if self.__key_join_flag in keystr:
    #         flag = True
    #     return flag
    #
    # def __check_key_count(self, keystr: str):
    #     """检测dependence_key 及 replace_key 的值的个数"""
    #     """由于是在if else语句里调用的，也就是说进行了判断，传入的参数值不可能为None"""
    #     return len(keystr.split(self.__key_join_flag))



